﻿namespace DateControls
{
    using System;
    using System.ComponentModel;
    using System.Text;
    using System.Web.UI.WebControls;

    /// <summary>
    /// Calendar control.
    /// </summary>
    public class DatePicker : DateTextBox
    {
        private string dateFormat = "%d/%m/%Y";

        /// <summary>
        /// The location of the calendar img.
        /// </summary>
        private const string CALENDAR_IMG_RESOURCE = "DateControls.Resources.calendar_img.gif";

        /// <summary>
        /// The location of the Calendar javascript file.
        /// </summary>
        private const string CALENDAR_JS_RESOURCE = "DateControls.Resources.calendar.js";

        /// <summary>
        /// The location of the Calendar localization JS.
        /// </summary>
        private const string CALENDAR_LOCAL_RESOURCE = "DateControls.Resources.calendar-en.js";

        /// <summary>
        /// The location of the Calendar setup js.
        /// </summary>
        private const string CALENDAR_SETUP_RESOURCE = "DateControls.Resources.calendar-setup.js";
        
        /// <summary>
        /// Gets or sets the calendar button.
        /// </summary>
        /// <value>The calendar button.</value>
        private Image CalendarButton { get; set; }

        /// <summary>
        /// Gets the name of the close calendar function.
        /// </summary>
        /// <value>The name of the close calendar function.</value>
        private string OnCloseCalendarFunctionName
        {
            get { return string.Format("{0}_{1}","closeCalendar", base.ClientID); }
        }

        /// <summary>
        /// Gets or sets the date format of the calendar.
        /// For example %d %m, %Y(Day/Month/Year)
        /// </summary>
        /// <value>The date format.</value>
        [Category("Layout")]
        [Description("Gets or sets the date format of the calendar. For example %d %m, %Y(Day/Month/Year)")]
        public virtual string DateFormat
        {
            get { return dateFormat; }
            set { dateFormat = value; }
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Creates the Calendar Button Image
            this.CalendarButton = this.CreateAndSetCalendarButton();

            this.Controls.Add(this.CalendarButton);
        }

        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            base.Render(writer);

            this.EnsureChildControls();
            // renders the button calendar.
            this.CalendarButton.RenderControl(writer);

            // if hte control is enabled
            if (this.Enabled)
            {
                // renders the calendar setup.
                string calendarSetup = this.GetCalendarSetup();
                writer.Write(calendarSetup);
            }
        }

        /// <summary>
        /// Gets the additional jquery scripts(used for customization).
        /// </summary>
        /// <returns>Returns a script which allow to reset the textbox when the calendar image is clicked.</returns>
        protected override string GetAdditionalJqueryScripts()
        {
            var sb = new StringBuilder();
            // 
            sb.AppendFormat("$(\"#{0}\").click(function()", this.CalendarButton.ClientID);
            sb.Append("     {");
            sb.AppendFormat("   $(\"#{0}\").val(\"\");", base.ClientID);
            sb.AppendFormat("   if ($(\"#{0}\").hasClass(\"{1}\"))", base.ClientID, base.ErrorCSSClass);
            sb.Append("         {");
            sb.AppendFormat("      $(\"#{0}\").removeClass(\"{1}\");", base.ClientID, base.ErrorCSSClass);
            sb.Append("         }");
            sb.Append("      });");

            return sb.ToString();
        }

        protected override void OnInit(EventArgs e)
        {
            this.RegisterScripts();
            base.OnInit(e);
        }
        
        /// <summary>
        /// Sets additional setup to the calendar.
        /// </summary>
        /// <param name="calendarSetup">The calendar setup.</param>
        protected virtual void SetAdditionalCalendarSetup(StringBuilder calendarSetup)
        {
            // do nothing.       
        }

        private ImageButton CreateAndSetCalendarButton()
        {
            var calBtn = new ImageButton();
            calBtn.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), CALENDAR_IMG_RESOURCE);

            calBtn.BorderWidth = 0;
            calBtn.ID = this.ID + "Calendar_Button";

            return calBtn;
        }

        /// <summary>
        /// This javascript function is called when the field is required(IsValidEmpty="false").
        /// This ensures that the calendar when is closed with no date selected, the error css class
        /// is added to the text box.
        /// </summary>
        /// <returns>A Javascript function.</returns>
        private string GetOnCloseFunctionJs()
        {
            var sb = new StringBuilder();
            sb.AppendFormat("     function {0}(calendar)", this.OnCloseCalendarFunctionName);
            sb.Append("           {");
            sb.AppendFormat("        var date = $(\"#{0}\").val();", base.ClientID);
            sb.Append("              if (date.length == 0){");
            sb.AppendFormat("           $(\"#{0}\").addClass(\"{1}\");", base.ClientID, base.ErrorCSSClass);
            sb.Append("              }");
            sb.Append("              calendar.hide();");
            sb.Append("           }");

            return sb.ToString();
        }

        /// <summary>
        /// Gets the calendar setup string.
        /// </summary>
        /// <returns></returns>
        private string GetCalendarSetup()
        {
            var sb = new StringBuilder();

            sb.Append("<script type='text/javascript'>");
            sb.Append("Calendar.setup({");
            sb.AppendFormat("inputField : '{0}',", base.ClientID); // id of the input field
            sb.AppendFormat("ifFormat : '{0}',", this.DateFormat); // format of the input field
            sb.AppendFormat("daFormat : '{0}',", this.DateFormat);// format of the displayed date
            sb.AppendFormat("button : '{0}',", this.CalendarButton.ClientID); // trigger for the calendar (button ID)
            sb.Append("align : 'Tr',"); //  alignment (defaults to "Bl")
            
            // if is required we need to perform some extra work when the calendar is closed.
            if (!base.IsValidEmpty)
            {
                sb.AppendFormat("onClose : {0},", this.OnCloseCalendarFunctionName);
            }

            this.SetAdditionalCalendarSetup(sb);
            sb.Append("singleClick : true");
            sb.Append("});");
            sb.Append("</script>");

            return sb.ToString();
        }

        /// <summary>
        /// Register the required javascript functions/includes.
        /// </summary>
        private void RegisterScripts()
        {
            if (!Page.ClientScript.IsClientScriptIncludeRegistered("calendarJs"))
            {
                this.RegisterCalendarJs();
            }

            if (!Page.ClientScript.IsClientScriptIncludeRegistered("calendarLangJs"))
            {
                this.RegisterCalendarLangJs();
            }

            if (!Page.ClientScript.IsClientScriptIncludeRegistered("calendarSetupJs"))
            {
                this.RegisterCalendarSetupJs();
            }

            // if is required, renders the closeCalendar function to validate when the calendar is shown, some date
            // must be selected
            if (!base.IsValidEmpty)
            {
                this.RegisterOnCloseCalendarJs();
            }
        }

        private void RegisterOnCloseCalendarJs()
        {
            // gets and register the function used for the calendar when is clicked.
            string onCloseCalendarJs = this.GetOnCloseFunctionJs();
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), this.OnCloseCalendarFunctionName, onCloseCalendarJs, true);
        }

        private void RegisterCalendarSetupJs()
        {
            // gets the calendar-setup.js url(embedded resource)
            string calendarSetupJsUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), CALENDAR_SETUP_RESOURCE);
            Page.ClientScript.RegisterClientScriptInclude("calendarSetupJs", calendarSetupJsUrl);
        }

        private void RegisterCalendarLangJs()
        {
            // gets the localization js of the calendar(embedded resource)
            string calendarLang = Page.ClientScript.GetWebResourceUrl(this.GetType(), CALENDAR_LOCAL_RESOURCE);
            Page.ClientScript.RegisterClientScriptInclude("calendarLangJs", calendarLang);
        }

        private void RegisterCalendarJs()
        {
            // gets the calendar.js url(embedded resource)
            string calendarJsUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), CALENDAR_JS_RESOURCE);
            Page.ClientScript.RegisterClientScriptInclude("calendarJs", calendarJsUrl);
        }
    }
}